package io.gitee.minelx.commontools.date;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;

import static io.gitee.minelx.commontools.date.Elapses._offset;

public class Clock {
    private final Calendar self;
    private final ClockValues values;

    Clock(Calendar self) {
        this.self = self;
        values = ClockValues.fromCalendar(self);
    }

    public Duration to(Clock another) {
        return Duration.between(this, another);
    }

    Clock cast(ClockUnit unit) {
        int[] range = values.range(unit.ordinal() + 1);
        return unit.at(range);
    }

    public Clock ceil(ClockUnit unit) {
        boolean allDefaultAndNoNeedToCeil = unit.levelsBelow().stream()
                .allMatch(level -> level.isDefaultValue(self));
        if (allDefaultAndNoNeedToCeil) {
            return this;
        }
        // do ceil
        return cast(unit).elapse(_offset(1, unit));
    }

    public Day day() {
        return new Day(this);
    }

    public Month month() {
        return new Month(this);
    }


    // FIXME wait a moment...    more creations of classes
    Stream<Clock> offsets(ClockUnit unit, int amount) {
        return Stream.iterate(this,
                each -> each.elapse(_offset(amount, unit)));
    }

    public String format(String pattern) {
        return new SimpleDateFormat(pattern)
                .format(self.getTime());
    }

    public Clock elapse(Consumer<Calendar> elapsing) {
        Calendar copied = Clocks.copy(self);
        elapsing.accept(copied);
        return new Clock(copied);
    }

    public ClockValues values() {
        return values;
    }

    Calendar self() {
        return self;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Clock clock = (Clock) o;
        return self.equals(clock.self) && values.equals(clock.values);
    }

    @Override
    public int hashCode() {
        return Objects.hash(self, values);
    }

    @Override
    public String toString() {
        return "Clock{" +
                "values=" + values +
                '}';
    }
}
